package com.yichat.micro.msghttpservice.core.okHttp;

import com.yichat.micro.msghttpservice.common.Constants;
import com.yichat.micro.msghttpservice.common.result.HttpResult;
import com.yichat.micro.msghttpservice.common.result.OkHttpResult;
import com.yichat.micro.msghttpservice.common.util.JsonUtil;
import com.yichat.micro.msghttpservice.common.util.StringUtil;
import com.yichat.micro.msghttpservice.common.vo.MsgHttpRequestVo;
import com.yichat.micro.msghttpservice.core.base.AbstractHttpCaller;
import com.yichat.micro.msghttpservice.core.retry.RetryMsgHttpRequestVo;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Created by chenmohan on 2019/03/28
 *
 * @description
 */
@Component("okHttp")
@Slf4j
public class OkHttpCaller extends AbstractHttpCaller<Request, Response> {

    @Autowired
    OkHttpClient okHttpClient;


    @Override
    protected HttpResult doCall(Request request, RetryMsgHttpRequestVo retryRequestParam, Long sid) throws TimeoutException {
        HttpResult httpResult = httpRequest(request, retryRequestParam);
        return httpResult;
    }

    @Override
    protected boolean isRequestSuccessful(MsgHttpRequestVo request, HttpResult httpResult) {
        //如果response为空,说明是当前服务请求远程接口时服务本地timeout,需要做重试操作
        if (httpResult == null) {
            return false;
        }
        String reqResult = request.getReqResult();
        if (StringUtil.isEmpty(reqResult)) {
            return status_ok == httpResult.getCode() || status_async_commmit == httpResult.getCode();
        }
        return status_async_commmit == httpResult.getCode()
                || (status_ok == httpResult.getCode()
                && StringUtil.equals(reqResult.trim(), httpResult.getData().trim()));
    }

    @Override
    protected HttpResult timeout(RetryMsgHttpRequestVo retryRequestParam) {
        return HttpResult.timeout(retryRequestParam);

    }

    /**
     * 构造Request体
     *
     * @param requestParam
     * @return
     */
    @Override
    protected Request buildRequest(MsgHttpRequestVo requestParam) {
        Request.Builder reqBuilder = new Request.Builder();
        reqBuilder.url(requestParam.getUrl())
                .addHeader("Connection", "close");
        RequestBody requestBody = null;
        switch (requestParam.getMethod()) {
            case GET:
                HttpUrl.Builder urlBuilder = new HttpUrl.Builder();
                Map<String, String> headerParamMap = JsonUtil.toMap(requestParam.getData());
                headerParamMap.entrySet().parallelStream().forEach(e -> {
                    urlBuilder.addQueryParameter(e.getKey(), e.getValue());
                });
                reqBuilder.url(urlBuilder.build());
                reqBuilder.get();
                break;
            case POST:
                requestBody = RequestBody.create(MediaType.parse(requestParam.getMediaType()), requestParam.getData());
                reqBuilder.post(requestBody);
                break;
            case POST_FORM:
                FormBody.Builder formBuilder = new FormBody.Builder();
                Map<String, String> bodyParamMap = JsonUtil.toMap(requestParam.getData());
                //fixbug: 表单kv值只能同步添加,内部的接收kv对象的集合是ArrayList,是线程不安全的
                bodyParamMap.entrySet().stream().forEach(e -> {
                    formBuilder.add(e.getKey(), String.valueOf(e.getValue()));
                });
                reqBuilder.post(formBuilder.build());
                break;
            case PUT:
                //TODO put
                break;
            case DELETE:
                //TODO delete
                break;
            default:
                break;
        }
        return reqBuilder.build();
    }

    private HttpResult httpRequest(Request request, RetryMsgHttpRequestVo retryRequestParam) {
        MsgHttpRequestVo requestParam = retryRequestParam.getVo();
        Call call = okHttpClient.newCall(request);
        call.timeout().timeout(requestParam.getTimeout(), TimeUnit.MILLISECONDS);
        long sid = retryRequestParam.getSid();
        HttpResult[] httpResults = new HttpResult[1];
        if (Constants.enalbe == requestParam.getSync()) {
            try {
                Response response = call.execute();
                HttpResult httpResult = OkHttpResult.resp(response, retryRequestParam);
                callBack(requestParam, httpResult, sid);
                return httpResult;
            } catch (IOException e) {
                log.error("{} 同步请求异常了", StringUtil.isEmpty(requestParam.getUrl()) ? retryRequestParam.getList().get(retryRequestParam.getCurrentListIndex()).getUrl() : requestParam.getUrl());
                return timeout(retryRequestParam);
            }
        } else {
            call.enqueue((new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    log.warn("异步请求异常了", e.getMessage());
                    httpResults[0] = timeout(retryRequestParam);
                    //http请求框架自己的异步处理,需要手动将请求任务丢到重试队列中
                    retryManager.delayAndRetry(retryRequestParam);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    response.header("buz", requestParam.getBuz());
                    //如果有回调url则进行回调
                    callBack(requestParam, OkHttpResult.resp(response, retryRequestParam), sid);
                    //      record(requestParam, responses[0], sid);
                }
            }));
        }

        //responses[0] = OkHttpResponseBuilder.asnycCommit(request, retryRequestParam);
        httpResults[0] = OkHttpResult.syncCommit(retryRequestParam);
        return httpResults[0];
    }

    /**
     * 回调结果
     *
     * @param requestParam
     */
    private boolean callBack(MsgHttpRequestVo requestParam, HttpResult httpResult, Long sid) {
        if (StringUtil.isNotEmpty(requestParam.getCallback())) {
            MsgHttpRequestVo parameter = buildCallbackRequestParameter(requestParam);
            parameter.setData(httpResult.getData());
            parameter.setBuz(requestParam.getBuz());
            parameter.setSync(Constants.enalbe);
            parameter.setReqResult(super.callbackSuccessResponse);
            parameter.setReTry(0);
            HttpResult callBackResult = process(retryManager.wrap(parameter, sid));
            String data = callBackResult.getData();
            if (Constants.ok.equals(data)) {
                log.error("\n回调结果为 => data = {}", data);
                return true;
            }
        }
        return false;
    }

}
